package cc.langk.core.db;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.regex.Pattern;

/**
 * TODO SQL 语句解析
 */
@SuppressWarnings("all")
public class SqlParse {
    private static final long serialVersionUID = 1L;
    private static Log log = LogFactory.getLog(SqlParse.class);

    private static final String REGEX_SQL_KEYWORD = "('.,)|(;)|(:')|(:--)|(\\b(select|update|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)";
    private static Pattern pattern = Pattern.compile(REGEX_SQL_KEYWORD, Pattern.CASE_INSENSITIVE);

    /**
     * TODO 解析 page内参数
     */
    public static String parse(Map<String, Object> whereMap, List<Object> param, Query query) {
        query.init(); // 初始化 参数
        return createSql(whereMap, param);
    }

    /**
     * TODO 解析 Query 内参数
     */
    public static String parseQuery(Map<String, Object> whereMap, List<Object> param) {
        return createSql(whereMap, param);
    }

    /**
     * 统一解析 SQL 语句 1.0 SQL 过滤 2.0 优化字符串分解效率 common.StringUtil
     *
     * @author ouyang 创建时间：2012-03-13 01:14:44
     */
    protected static String createSql(Map<String, Object> whereMap, List<Object> param) {
        StringBuffer sql = new StringBuffer(100); // 最后生成的SQL
        try {
            String columnName = ""; // 解析字段名
            String columnNamePrefix = "";// 解析前缀
            boolean flag_add_value = true; // 对于 not null等 取值为 false 不需要加入参数值
            String flag_like = ""; // like 查询的分类 all right left

            Map<String, String> findOR = new HashMap<String, String>(); // or
            // 查询的分组查询
            // 组名 ，(a=b and b=c and b=c)
            Map<String, String> findORLike = new HashMap<String, String>(); // or
            // 查询的分组查询
            // 组名 ，(a like '?' and b like '?' and b like '?')

            if (whereMap == null || whereMap.isEmpty()) {
                log.info("没有查询条件，无法组装 SQL 语句------------------");
                param = Collections.EMPTY_LIST;
                return "";
            }

            // 修改时 备注 FindType.LIKE key 要取值 为带下划线的 FindType.LIKE_
            int i = 0;
            for (Map.Entry<String, Object> e : whereMap.entrySet()) {
                if (e.getKey() != null && e.getValue() != null
                        && e.getValue().toString().length() > 0 && !e.getKey().startsWith(FindType.NO_CONTANT)) {
                    flag_like = ""; // 是否为like 查询
                    flag_add_value = true; // 需要添加参数值
                    if (e.getKey().contains(FindType.FIND_FLAG)) { // 特殊查询
                        columnName = StringUtils.split(e.getKey(), FindType.FIND_FLAG)[1];
                        columnNamePrefix = StringUtils.split(e.getKey(), FindType.FIND_FLAG)[0] + FindType.FIND_FLAG; // 拼接前缀
                        if (FindType.LIKE_.equals(columnNamePrefix)) {
                            flag_like = FindType.ALL;
                            sql.append(" AND " + columnName + " LIKE {" + i + "}");
                        } else if (FindType.LIKE_LEFT_.equals(columnNamePrefix)) { // 左
                            // like
                            flag_like = FindType.LEFT;
                            sql.append(" AND " + columnName + " LIKE {" + i + "}");
                        } else if (FindType.LIKE_RIGHT_.equals(columnNamePrefix)) {// 右
                            // like
                            flag_like = FindType.RIGHT;
                            sql.append(" AND " + columnName + " LIKE {" + i + "}");
                        } else if (FindType.IN_.equals(columnNamePrefix)) { // 解决jdbc bug in不用？处理
                            sql.append(" AND " + columnName + " IN ( " + e.getValue() + " ) ");
                            flag_add_value = false;
                        } else if (FindType.IS_NULL_.equals(columnNamePrefix)) {
                            sql.append(" AND " + columnName + " IS NULL ");
                            flag_add_value = false;
                        } else if (FindType.NOT_NULL_.equals(columnNamePrefix)) {
                            sql.append(" AND " + columnName + " IS NOT NULL");
                            flag_add_value = false; // 不为 null
                        } else if (FindType.NOT_EQUAL_.equals(columnNamePrefix)) {
                            sql.append(" AND " + columnName + " <> {" + i + "}");// 不等于
                        } else if (FindType.NOT_IN_.equals(columnNamePrefix)) {// 解决 jdbc bug in 不用 ？处理
                            sql.append(" AND " + columnName + " NOT IN( " + e.getValue() + " )");
                            flag_add_value = false;
                        } else if (FindType.MORE_.equals(columnNamePrefix)) {
                            sql.append(" AND " + columnName + " > {" + i + "}");
                        } else if (FindType.LESS_.equals(columnNamePrefix)) {
                            sql.append(" AND " + columnName + " < {" + i + "}");
                        } else if (FindType.MORE_EQUAL_.equals(columnNamePrefix)) {
                            sql.append(" AND " + columnName + " >= {" + i + "}");
                        } else if (FindType.LESS_EQUAL_.equals(columnNamePrefix)) {
                            sql.append(" AND " + columnName + " <= {" + i + "}"); // 小于等于
                        }
                    } else if (e.getKey().contains(FindType.OR_FLAG)) { // 是否为or的分组查询默认的 =
                        String groupName = StringUtils.split(e.getKey(), FindType.OR_FLAG)[0];
                        String cName = StringUtils.split(e.getKey(), FindType.OR_FLAG)[1]; // 真实的字段名
                        String sourceValue = ""; // 该分组是否存在 原有值
                        if (findOR.containsKey(groupName)) {
                            sourceValue = findOR.get(groupName);
                        }
                        findOR.put(groupName, sourceValue + (sourceValue.length() > 0 ? "," : "") + cName + "=" + e.getValue());
                        flag_add_value = false; // 不需要添加查询值
                    } else if (e.getKey().contains(FindType.LIKE_OR_FLAG)) { // 是否为or的分组查询支持like
                        String groupName = StringUtils.split(e.getKey(), FindType.LIKE_OR_FLAG)[0];
                        String cName = StringUtils.split(e.getKey(), FindType.LIKE_OR_FLAG)[1]; // 真实的字段名
                        String sourceValue = ""; // 该分组是否存在 原有值
                        if (findORLike.containsKey(groupName)) {
                            sourceValue = findORLike.get(groupName);
                        }
                        findORLike.put(groupName, sourceValue + (sourceValue.length() > 0 ? "," : "") + cName + "=" + e.getValue());
                        flag_add_value = false; // 不需要添加查询值
                    } else { // 默认为 等号
                        columnName = e.getKey();
                        sql.append(" AND " + columnName + " = {" + i + "}");
                    }

                    if (flag_add_value) { // 是否初始化
                        param = param == null ? new ArrayList<Object>() : param;
                        if (FindType.ALL.equals(flag_like)) { // 是否为 like 查询
                            param.add("%" + e.getValue() + "%");
                        } else if (FindType.LEFT.equals(flag_like)) { // 左 like查询
                            param.add("%" + e.getValue());
                        } else if (FindType.RIGHT.equals(flag_like)) { // 右 like查询
                            param.add(e.getValue() + "%");
                        } else {
                            param.add(e.getValue()); // 参数对应的值
                        }
                    }
                    i++;
                }
            }
            if (findOR.size() > 0) { // 需要解析 or 分组查询 (支持不同 字段名称 的 or 分组查询)
                for (Map.Entry<String, String> e : findOR.entrySet()) {
                    String valueString = e.getValue();
                    String[] currentGroup = StringUtils.split(valueString, ",");
                    sql.append(" AND ( "); // 分组的开始
                    for (int j = 0; j < currentGroup.length; j++) { // 是否包含多个值
                        if (currentGroup[j] != null && currentGroup[j].length() > 0) {
                            if (j > 0) {
                                sql.append(" OR ");
                            }
                            sql.append(StringUtils.split(currentGroup[j], "=")[0] + " = {" + i + "}");
                            param.add(StringUtils.split(currentGroup[j], "=")[1]);
                        }
                    }
                    sql.append(" ) "); // 分组的结束
                }
            }
            if (findORLike.size() > 0) { // 需要解析 or 分组查询 (支持不同 字段名称 的 or 分组查询)
                for (Map.Entry<String, String> e : findORLike.entrySet()) {
                    String valueString = e.getValue();
                    String[] currentGroup = StringUtils.split(valueString, ",");
                    sql.append(" AND ( "); // 分组的开始
                    for (int j = 0; j < currentGroup.length; j++) { // 是否包含多个值
                        if (currentGroup[j] != null && currentGroup[j].length() > 0) {
                            if (j > 0) {
                                sql.append(" OR ");
                            }
                            sql.append(StringUtils.split(currentGroup[j], "=")[0] + " LIKE {" + i + "}");
                            param.add("%" + StringUtils.split(currentGroup[j], "=")[1] + "%");
                        }
                    }
                    sql.append(" ) "); // 分组的结束
                }
            }
            // ---释放资源----
            findOR.clear();
            findORLike.clear();
            if (pattern.matcher(sql.toString()).find()) {
                log.error("请求参数包含非法参数--------------------------");
                throw new RuntimeException("非法参数" + sql.toString());
            }
        } catch (Exception e) {
            log.error("解析sql 语句 出错", e);
        }
        return sql.toString();

    }

    /**
     * 检测是否包含SQL 特殊字符
     *
     * @param str
     */
    public static void isRisk(String str) {
        if (pattern.matcher(str).find()) {
            log.error("请求参数包含非法参数--------------------------");
            throw new RuntimeException("非法参数" + str);
        }
    }
}
